package cn.ljy.authority.config.security.filter;

import cn.ljy.authority.config.security.MySecurityConfig;
import cn.ljy.authority.config.security.TokenUtil;
import cn.ljy.authority.model.dto.SysUserAuthority;
import cn.ljy.authority.service.SysUserTokenService;
import cn.ljy.authority.service.TokenService;
import cn.ljy.authority.service.impl.ISysUserService;
import cn.ljy.common.model.result.ResponseData;
import cn.ljy.common.model.result.ResultCode;
import cn.ljy.common.utils.ResponseUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Token过滤器，对登录状态进行验证
 * 说明：配合spring security使用，主要用于验证token的值是否合法，不要对登录状态进行验证，登录状态交给springsecurity自带拦截器进行操作
 * 设计意图：
 * 1.如果token不为空，则对token进行验证，
 *（1）如果符合规则，则根据token获得用户对象和相应的权限集合并传递到spring secirity的上下文中保存（如果spring security设置为不保存session，则只对本次请求有效），然后传递到spring security的下一个拦截器进行验证
 *（2）如果不符合规则则返回异常信息
 * 2.如果token为空，则不进行拦截(如果不清楚规则看如下解释)
 * 虽然是不拦截，但因为没有传递用户信息到spring security的下一个拦截器（即登录状态验证）
 * 因此会被spring security的后续验证被判定为未登录
 * （1）如果在spring security对本次请求的地址配置了permitAll，则不会对该请求进行登录状态验证
 * （2）如果在spring security对本次请求的地址配置了ignoring，则不会对该请求进行登录状态验证
 * （3）即便设置了上述的放行(permitAll)或不拦截(ignoring)设置，但是如果请求有权限的设定
 *  @PreAuthorize("hasAuthority('sys:aa')") 则spring security也会进行拦截.
 *  如果是设置了放行，没有携带token时会提示没有相关权限
 *  如果是设置了不拦截(包括放行)，没有携带token时会提示没有登录
 *
 * @author ljy
 * 20220129创建
 * 20221201最后修改
 */
// 该过滤器是用来配合spring security做拦截的，如果使用@Component来创建过滤器，那么spring security的配置，如ignoring().antMatchers做出的不做拦截的网址将不会生效，即过滤器还是拦截所有网址
// 即：因为我们将Filter作为Bean注入后，Spring会自动将其管理，即使你在Security中配置了忽略，Spring还是照样会将请求使用过滤器进行过滤
// 具体使用方法参考WebSecurityConfig中的方法，直接通过new MyTokenFilter(sysUserTokenService,iSysUserService,tokenService)来创建对象才能生效
// @Component
@Slf4j
@RequiredArgsConstructor//以final修改的变量作为构造函数
public class MyTokenFilter extends OncePerRequestFilter {

//	@Autowired
	private final SysUserTokenService sysUserTokenService;
//	@Autowired
	private final ISysUserService iSysUserService;
//	@Autowired
	private final TokenService tokenService;

	private List<String> urlOfIgnore=new ArrayList<String>(){{
		//add(MySecurityConfig.ADMINLOGINURL);//管理员登录地址，要拦截进行验证码检查
		add(MySecurityConfig.ADMINLOGOUTURL);//管理员注销地址
//		add(MySecurityConfig.VERIFYCODEURL);//管理员验证码地址
	}};//不拦截的请求路径，即不验证token

	@Override
	public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		//获取请求地址
		String servletPath=request.getServletPath();
		if(urlOfIgnore.contains(servletPath)){
			filterChain.doFilter(request, response);
		}else if(servletPath.contains(MySecurityConfig.ADMINLOGINURL)){ // 如果是登录请求检验
			String token = request.getParameter("token");//获取服务器的token
			String verifyCode = request.getParameter("verifyCode");//获取服务器的验证码
			try {
				//检查验证码是否正确，如果错误会抛出异常
				tokenService.verifyVerifyCodeToken(token,verifyCode);
				filterChain.doFilter(request, response);
			} catch (Exception e) {
				//因为是过滤器，异常捕获器无法捕获这里的错误信息，要手动抓取并返回错误信息
				ResponseUtil.responseJson(response, ResponseData.error(ResultCode.UNAUTHORIZED.getCode(),e.getMessage()));
			}
		}else{
			String token = TokenUtil.getToken(request);//读取前端传递过来的token
			if (StringUtils.hasLength(token)) {
				try {
					//验证并刷新令牌持续时间
					sysUserTokenService.verifyAndRefreshUserToken(token);
					//从token中加载所有权限信息
					String username= sysUserTokenService.getUsername(token);
					SysUserAuthority userAuthority = (SysUserAuthority)iSysUserService.loadUserByUsername(username);
					/*userAuthority.getAuthorities().stream().forEach(permission -> {
						log.debug("拥有权限"+permission);
					});*/
					if(userAuthority!=null){
						//封装权限对象
						UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userAuthority,
								null, userAuthority.getAuthorities());
						//将权限信息封装到spring security的上下文。
						//特别注意：如果spring security已经设置了不保存session，则该上下文只在本次请求访问的时候生效。
						SecurityContextHolder.getContext().setAuthentication(authentication);
						//传递到spring security的下一个权限验证器
						filterChain.doFilter(request, response);
					}
				} catch (Exception e) {
					ResponseUtil.responseJson(response, ResponseData.error(ResultCode.UNAUTHORIZED.getCode(),e.getMessage()));
//					ResponseUtil.responseJson(response, ResultCode.UNAUTHORIZED.getCode(), e.getMessage());
				}
			}else{//如果token为空，则直接过滤，在spring security的自带拦截器中会进行登录状态验证
				//ResponseUtil.responseJson(response, ResultCode.UNAUTHORIZED.getCode(), "请先登录");
				filterChain.doFilter(request, response);
			}
		}
	}
}
